/// $Id$
// Author : Anushi Shah

#include <string>
using namespace std;

#include "ace/Get_Opt.h"  // for parsing command line
#include "ace/Log_Msg.h"  // for logging messages
#include "ace/Sched_Params.h"  // for scheduling params
#include "ace/Task.h"  // for the main thread of control

#include "tao/ORB_Core.h" // the ORB itself
#include "tao/RTCORBA/RTCORBA.h"  // all the RTCORBA capabilities
#include "tao/RTPortableServer/RTPortableServer.h" // for RT POA

#include "TurnaroundActuator_i.h"  // our implementation header
#include "Utils.h"

/**************************************************************************/
// files where the IOR will be output
const ACE_TCHAR *dir = ACE_TEXT ("..");
const ACE_TCHAR *ac_file = ACE_TEXT("actuator");
CORBA::Short poa_priority = 5;  // poa level priority
CORBA::Short obj_priority = 10;  // obj level priority
string ac_ior_file;

/**************************************************************************/
/// parse arguments
int parse_args (int argc, ACE_TCHAR *argv[])
{
  // a utility provided in ACE
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:o:p:"));
  int c, result;

  while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k': // directory where ior is generated. Default is one level up
        dir = get_opts.opt_arg ();
        break;

      case 'o':  // indicates priority level for obj to be executed at
        result = ::sscanf (ACE_TEXT_ALWAYS_CHAR (get_opts.opt_arg ()),
                           "%hd",
                           &obj_priority);
        if (result == 0 || result == EOF)
          ACE_ERROR_RETURN ((LM_ERROR,
                             "Unable to process <-o> option"),
                            -1);
        break;

      case 'p':  // indicates priority level for POA
        result = ::sscanf (ACE_TEXT_ALWAYS_CHAR (get_opts.opt_arg ()),
                           "%hd",
                           &poa_priority);
        if (result == 0 || result == EOF)
          ACE_ERROR_RETURN ((LM_ERROR,
                             "Unable to process <-p> option"),
                            -1);
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <directory of IOR file> "
                           "-o <obj-level priority> "
                           "-p <POA-level priority> "
                           "\n",
                           argv [0]),
                          -1);
      }

  // create the full file names for ior files

  ac_ior_file = 
    string (dir) + 
    ACE_DIRECTORY_SEPARATOR_CHAR +
    string (ac_file);
  return 0;
}

/**************************************************************************/
/// For some odd reason the RT capabilities cannot be demonstrated without
/// first spawning a thread and then doing all the RTCORBA stuff in there. It
/// just does not work in the main () thread of control -- neither on Windows
/// nor on Linux.  As a result, I had to resort to first creating a thread
/// using the ACE_Task class.
class Task : public ACE_Task_Base
{
public:

  // constructor
  Task (ACE_Thread_Manager &thread_manager,
        CORBA::ORB_ptr orb);

  // this is where all the logic for the thread goes in
  int svc (void);

private:
  CORBA::ORB_var orb_;

};

// constructor
Task::Task (ACE_Thread_Manager &thread_manager,
            CORBA::ORB_ptr orb)
  : ACE_Task_Base (&thread_manager),
    orb_ (CORBA::ORB::_duplicate (orb))
{
}

// main logic
int
Task::svc (void)
{
  try
    {
      // we came here because of the call to "activate" in main program.  4
      // steps were done in main (). The remainder are done here.
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : Inside the SVC meithod\n")));

      /*****************************************************/
      /*   Step 5: Obtain the RT ORB                       */
      /*****************************************************/
      // now let us acquire the real-time ORB. Remember that the string passed
      // to resolve_initial_references is well-known  
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : acquire the RTORB\n")));
      CORBA::Object_var object =
        this->orb_->resolve_initial_references ("RTORB");

      // defensive programming - check for errors
      if (CORBA::is_nil (object.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Actuator : Cannot acquire RTORB\n")));
        return 1;
      }

      // we must now narrow it to the right type
      RTCORBA::RTORB_var rt_orb = RTCORBA::RTORB::_narrow (object.in ());

      // defensive programming - check for errors
      if (CORBA::is_nil (rt_orb.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Actuator : Cannot narrow to RTORB\n")));
        return 1;
      }

      /*****************************************************/
      /*   Demo Step showing a disallowed functionality    */
      /*****************************************************/
      // this block of code is for demo purpose only to show that we cannot
      // stringify the RTORB's reference.
      try 
        {
          ACE_DEBUG ((LM_DEBUG,
                      ACE_TEXT ("Actuator: demonstrating an exception\n")));
          // let us try to stringify ref of RTORB, which should throw an
          // exception called MARSHAL because RTORB is a local interface and
          // cannot be stringified or sent to remote location as a parameter.
          CORBA::String_var rtior = 
            this->orb_->object_to_string (rt_orb.in ());
        } 
      catch (const CORBA::Exception& e) 
        {
          e._tao_print_exception ("Exception caught");
          ACE_DEBUG ((LM_DEBUG,
                      ACE_TEXT ("Actuator: resuming from this exception\n")));
        }

      /*****************************************************/
      /*   Step 6: Acquire the Root POA                    */
      /*****************************************************/
      // next we should retrieve the RootPOA from the ORB. Note that the fact
      // we have managed to acquire a RTORB implies our ORB supports RT. As a
      // result, and by virtue of being compliant with the specification, the
      // RootPOA that we acquire next will automatically be "RT-enabled".
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator: acquire the RootPOA\n")));
      object =
        this->orb_->resolve_initial_references ("RootPOA");

      // defensive programming - check for errors
      if (CORBA::is_nil (object.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Actuator: Cannot acquire RootPOA\n")));
        return 1;
      }

      // remember to narrow it to the right type. If the following succeeds,
      // then it implies that the basic ORB gave us a RT-enabled RootPOA
      // (because it was RT-enabled).
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : narrow it to RT-enabled RootPOA\n")));
      RTPortableServer::POA_var root_poa =
        RTPortableServer::POA::_narrow (object.in ());

      // defensive programming - check for errors
      if (CORBA::is_nil (root_poa.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Actuator : Cannot narrow to RT-enabled POA\n")));
        return 1;
      }

      /*****************************************************/
      /*   Step 7: Acquire the POA Manager                 */
      /*****************************************************/
      // Now acquire the POA manager. This is needed for many things as we will
      // see shortly. For one, it maintains the state of each POA.
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : acquire a POA manager\n")));
      PortableServer::POAManager_var poa_manager =
        root_poa->the_POAManager ();

      /*****************************************************/
      /*   Step 8: Create child POA(s) with priority model */
      /*****************************************************/
      // our next step is to create a child POA that will serve client requests
      // at the server declared priority.  We do this inside of a utility
      // function we wrote to avoid code cluttering.
      RTPortableServer::POA_var child_rt_poa =  
        create_poa_with_client_declared_model (rt_orb.in (),
                                               root_poa.in (),
                                               poa_manager.in (),
                                               poa_priority);

      // defensive programming - check for errors
      if (CORBA::is_nil (child_rt_poa.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Actuator : Cannot acquire child rt poa\n")));
        return 1;
      }

      /*****************************************************/
      /*   Step 9: Create implementation instance          */
      /*****************************************************/
      // now instantiate our implementation class
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : instantiate our implementation\n")));
      Block_Turnaround_TurnaroundActuator_i *servant = 
        new Block_Turnaround_TurnaroundActuator_i (this->orb_.in (),
                               rt_orb.in ());

      // defensive programming - check for errors
      if (!servant) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Actuator : Cannot allocate an impl\n")));
        return 1;
      }

      /*****************************************************/
      /*   Step 10: Activate the implementation            */
      /*****************************************************/
      // We now demonstrate 3 registrations of the servant. One registered with
      // the root poa, one with the child poa but at the POA-level priority,
      // and one where we override the priority at the obj ref level. Note that
      // in all these cases the same implementation is chosen.

      // first one goes with root poa
      
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : create second obj reference with root POA\n")));
      if (create_and_publish_diag_object (this->orb_.in (),
                                          child_rt_poa.in (),
                                          servant,
                                          -1,
                                          ac_ior_file.c_str ()) == -1) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Acuator : Cannot create and register obj with rootPOA\n")));
        return 1;
      }
      
      /*****************************************************/
      /*   Step 11: Kickstart the POAs                      */
      /*****************************************************/
      // activate the POA so that it can start accepting requests.
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : activate the POA manager\n")));
      poa_manager->activate ();

      /*****************************************************/
      /*   Step 12: Hand over everything to the ORB        */
      /*****************************************************/
      // hand everything to the ORB
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : Now let the ORB take over\n")));
      this->orb_->run ();

    }
  catch (const CORBA::Exception& ex)
    {
      ex._tao_print_exception ("Exception caught");
      return -1;
    }

  // if and when the ORB returns, we get here
  return 0;

}

/**************************************************************************/
/// the main function. 
int
ACE_TMAIN(int argc, ACE_TCHAR *argv[])
{
  try
    {
      /*****************************************************/
      /*   Step 1: Get the ORB                             */
      /*****************************************************/
      // as the very first thing, let us acquire the ORB using the bootstrap
      // process of ORB_init
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : acquire the ORB\n")));
      CORBA::ORB_var orb =
        CORBA::ORB_init (argc, argv);
      
      // defensive programming - check for errors
      if (CORBA::is_nil (orb.in ())) {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Actuator : Cannot acquire ORB\n")));
        return 1;
      }

      /*****************************************************/
      /*   Step 2: check if priorities are supported       */
      /*****************************************************/
      // Make sure we can support multiple priorities that are required
      // for this test.
      if (!check_supported_priorities (orb.in ()))
        return 2;

      /*****************************************************/
      /*   Step 3: Parse the command line args             */
      /*****************************************************/
      // parse the arguments
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : parse arguments\n")));
      int result = parse_args (argc, argv);
      if (result != 0)
        return result;

      /*****************************************************/
      /*   Step 4: Create a task so that we can play around*/
      /*           with the RT CORBA operations            */
      /*****************************************************/
      // Thread Manager for managing task.
      ACE_Thread_Manager thread_manager;

      // Create task.
      Task task (thread_manager,
                 orb.in ());

      // Task activation flags. We create a lightweight process and wait to
      // have the thread send its status back.
      long flags =
        THR_NEW_LWP |
        THR_JOINABLE |
        orb->orb_core ()->orb_params ()->thread_creation_flags ();

      // Activate task (spawns a thread).  All the remaining steps are done in
      // the "svc" method of this task.
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : spawn a task\n")));
      result =
        task.activate (flags);
      if (result == -1)
        {
          // check what could have gone wrong. Maybe we did not have
          // permissions to run the real-time stuff.
          if (errno == EPERM)
            {
              ACE_ERROR_RETURN ((LM_ERROR,
                                 "Cannot create thread with scheduling policy %s\n"
                                 "because the user does not have the appropriate privileges, terminating program....\n"
                                 "Check svc.conf options and/or run as root\n",
                                 sched_policy_name (orb->orb_core ()->orb_params ()->ace_sched_policy ())),
                                2);
            }
          else
            // Unexpected error.
            ACE_ASSERT (0);
        }

      /*****************************************************/
      /*   Step 13: wait for thread to return (see svc)    */
      /*****************************************************/
      // Wait for task to exit.
      result =
        thread_manager.wait ();
      ACE_ASSERT (result != -1);
    }
  catch (const CORBA::Exception& ex)
    {
      ex._tao_print_exception ("Exception caught");
      return -1;
    }

  return 0;

}
